"""邮件服务"""

import random
from datetime import datetime, timedelta
from typing import Optional

from pydantic import EmailStr

from app.core.config import settings
from app.core.redis_conn import redis_conn
from app.utils.email_helper import email_helper


class EmailService:
    """邮件服务"""

    # Redis键前缀
    VERIFY_CODE_PREFIX = "email:verify_code:"
    VERIFY_CODE_LIMIT_PREFIX = "email:verify_code:limit:"

    # 验证码配置从settings获取
    VERIFY_CODE_EXPIRE_MINUTES = settings.VERIFY_CODE_EXPIRE_MINUTES
    VERIFY_CODE_LIMIT_MINUTES = settings.VERIFY_CODE_LIMIT_MINUTES
    VERIFY_CODE_LENGTH = settings.VERIFY_CODE_LENGTH

    @classmethod
    def generate_verify_code(cls) -> str:
        """生成验证码"""
        return "".join(random.choices("0123456789", k=cls.VERIFY_CODE_LENGTH))

    @classmethod
    async def get_verify_code(cls, email: str) -> Optional[str]:
        """获取验证码"""
        key = f"{cls.VERIFY_CODE_PREFIX}{email}"
        code = await redis_conn.get(key)
        return code if code else None  # redis_conn已经设置了decode_responses=True

    @classmethod
    async def set_verify_code(cls, email: str, code: str) -> None:
        """设置验证码"""
        key = f"{cls.VERIFY_CODE_PREFIX}{email}"
        await redis_conn.set(
            key,
            code,
            ex=timedelta(minutes=cls.VERIFY_CODE_EXPIRE_MINUTES),
        )

    @classmethod
    async def can_send_verify_code(cls, email: str) -> bool:
        """检查是否可以发送验证码"""
        key = f"{cls.VERIFY_CODE_LIMIT_PREFIX}{email}"
        return not await redis_conn.exists(key)

    @classmethod
    async def set_verify_code_limit(cls, email: str) -> None:
        """设置验证码发送限制"""
        key = f"{cls.VERIFY_CODE_LIMIT_PREFIX}{email}"
        await redis_conn.set(
            key,
            "1",
            ex=timedelta(minutes=cls.VERIFY_CODE_LIMIT_MINUTES),
        )

    @classmethod
    async def verify_code(cls, email: str, code: str) -> bool:
        """
        验证验证码
        :param email: 邮箱
        :param code: 验证码
        :return: 是否验证通过
        """
        key = f"{cls.VERIFY_CODE_PREFIX}{email}"
        stored_code = await redis_conn.get(key)
        if stored_code:
            await redis_conn.delete(key)  # 验证后立即删除验证码
            return stored_code == code
        return False

    @classmethod
    async def send_verify_code(cls, email: EmailStr, scene: str = "verify") -> bool:
        """
        发送验证码
        :param email: 邮箱
        :param scene: 场景(verify:验证邮箱, register:注册, reset_password:重置密码)
        :return: 是否发送成功
        """
        # 检查发送限制
        if not await cls.can_send_verify_code(str(email)):
            raise ValueError(f"请等待 {cls.VERIFY_CODE_LIMIT_MINUTES} 分钟后再试")

        # 生成验证码
        code = cls.generate_verify_code()

        # 根据场景设置不同的邮件内容
        subject_map = {
            "verify": "邮箱验证",
            "register": "注册验证码",
            "reset_password": "重置密码",
        }
        subject = subject_map.get(scene, "验证码")

        # 准备模板上下文
        context = {
            "code": code,
            "expire_minutes": cls.VERIFY_CODE_EXPIRE_MINUTES,
            "year": datetime.now().year,
            "company_name": settings.COMPANY_NAME,
        }

        # 发送邮件
        success = await email_helper.send_template_email(
            subject=f"{settings.PROJECT_NAME} - {subject}",
            recipients=str(email),
            template_name="verification",
            context=context,
        )

        if success:
            # 保存验证码
            await cls.set_verify_code(str(email), code)
            # 设置发送限制
            await cls.set_verify_code_limit(str(email))
            return True
        return False

    @classmethod
    async def send_welcome_email(cls, email: EmailStr, username: str) -> bool:
        """
        发送欢迎邮件
        :param email: 邮箱
        :param username: 用户名
        :return: 是否发送成功
        """
        context = {
            "username": username,
            "project_name": settings.PROJECT_NAME,
            "year": datetime.now().year,
            "company_name": settings.COMPANY_NAME,
        }

        return await email_helper.send_template_email(
            subject=f"欢迎加入 {settings.PROJECT_NAME}",
            recipients=str(email),
            template_name="welcome",
            context=context,
        )

    @classmethod
    async def send_reset_password_email(
        cls, email: EmailStr, username: str, reset_url: str
    ) -> bool:
        """
        发送重置密码邮件
        :param email: 邮箱
        :param username: 用户名
        :param reset_url: 重置密码链接
        :return: 是否发送成功
        """
        context = {
            "username": username,
            "reset_url": reset_url,
            "expire_hours": settings.PASSWORD_RESET_TOKEN_EXPIRE_HOURS,
            "year": datetime.now().year,
            "company_name": settings.COMPANY_NAME,
        }

        return await email_helper.send_template_email(
            subject=f"{settings.PROJECT_NAME} - 重置密码",
            recipients=str(email),
            template_name="reset_password",
            context=context,
        )

    